package logging

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
)

// TextFormatter 文本格式化工具
type TextFormatter struct {
}

// MaxLogMessageLen 文本日志的最大消息长度
const MaxLogMessageLen = 15000

// Format 格式化日志
func (f *TextFormatter) Format(rec *Record, buf *bytes.Buffer) (err error) {
	// 写入日志级别
	const severityChar = "DIWE"
	buf.WriteByte(severityChar[rec.Severity])

	// 写入时间
	_, month, day := rec.Time.Date()
	hour, minute, second := rec.Time.Clock()
	twoDigits(buf, int(month))
	twoDigits(buf, day)
	buf.WriteByte(' ')
	twoDigits(buf, hour)
	buf.WriteByte(':')
	twoDigits(buf, minute)
	buf.WriteByte(':')
	twoDigits(buf, second)
	buf.WriteByte('.')
	nDigits(buf, 6, uint64(rec.Time.Nanosecond()/1000), '0')
	buf.WriteByte(' ')

	// 写入线程编号
	nDigits(buf, 7, uint64(rec.Thread), ' ')
	buf.WriteByte(' ')

	{
		// 写入文件名
		file := rec.File
		if i := strings.LastIndex(file, "/"); i >= 0 {
			file = file[i+1:]
		}
		buf.WriteString(file)
	}

	buf.WriteByte(':')
	{
		// 写入行号
		var tmp [19]byte
		buf.Write(strconv.AppendInt(tmp[:0], int64(rec.Line), 10))
	}
	buf.WriteString("] ")

	// 格式化消息
	_, err = fmt.Fprintf(buf, rec.Format, rec.Args...)
	if err != nil {
		return err
	}

	// 截断消息
	if buf.Len() > MaxLogMessageLen-1 {
		buf.Truncate(MaxLogMessageLen - 1)
	}

	// 写入换行符
	if b := buf.Bytes(); b[len(b)-1] != '\n' {
		buf.WriteByte('\n')
	}

	return nil
}

// 数字
const digits = "0123456789"

// 二位数字
func twoDigits(buf *bytes.Buffer, d int) {
	buf.WriteByte(digits[(d/10)%10])
	buf.WriteByte(digits[d%10])
}

// 多位数字
func nDigits(buf *bytes.Buffer, n int, d uint64, pad byte) {
	var tmp [20]byte

	cutoff := len(tmp) - n
	j := len(tmp) - 1
	for ; d > 0; j-- {
		tmp[j] = digits[d%10]
		d /= 10
	}
	for ; j >= cutoff; j-- {
		tmp[j] = pad
	}
	j++
	buf.Write(tmp[j:])
}
